package com.fred.colorpicker

import android.content.Context
import android.graphics.Color
import android.graphics.LinearGradient
import android.graphics.Paint
import android.graphics.PointF
import android.graphics.Shader
import androidx.annotation.ColorInt
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.unit.IntSize
import com.fred.colorpicker.indicator.RectIndicator
import com.fred.colorpicker.indicator.RingIndicator
import kotlin.math.max
import kotlin.math.min

class Palette(context: Context,
              rectChanged: ((selectColor: Int, selectHsv: FloatArray) -> Unit)? = null,
              ringChanged: ((selectColor: Int, selectHsv: FloatArray) -> Unit)? = null) {

    private val x = mutableStateOf(-1f)
    private val y = mutableStateOf(-1f)

    private val huePaint: Paint by lazy {
        Paint(Paint.ANTI_ALIAS_FLAG)
    }
    private val saturationPaint: Paint by lazy {
        Paint(Paint.ANTI_ALIAS_FLAG)
    }
    private val borderPain by lazy {
        Paint(Paint.ANTI_ALIAS_FLAG).apply {
            color = Color.rgb(231, 231, 231)
        }
    }
    private val borderSize by lazy {
        context.dip(3F)
    }

    private val colors = intArrayOf(
        Color.RED,
        Color.YELLOW,
        Color.GREEN,
        Color.CYAN,
        Color.BLUE,
        Color.MAGENTA,
        Color.RED
    )

    private var size = IntSize.Zero

    private var bound  = Rect(0f,0f,0f,0f)

    private val rectIndicator by lazy {
        RectIndicator(context, rectChanged)
    }
    private val ringIndicator by lazy {
        RingIndicator(context, ringChanged)
    }

    fun updateState(size: IntSize) {
        if (this.size.width != size.width || this.size.height != size.height) {
            this.size = size
        }
        val width = size.width - bound.left - bound.right - 2*borderSize
        val height = size.height - bound.top - bound.bottom - 2*borderSize
        val hueShader = LinearGradient(0F, height, width, height, colors, null, Shader.TileMode.REPEAT)
        huePaint.shader = hueShader
        val start = borderSize+bound.left
        val saturationShader = LinearGradient(width, start, width, height + start,
                                              intArrayOf(Color.WHITE, Color.argb(0, 255, 255, 255), Color.BLACK),
                                              null,
                                              Shader.TileMode.REPEAT
        )
        saturationPaint.shader = saturationShader
        rectIndicator.initPosition(size.width, size.height)
        ringIndicator.initPosition(size.width, size.height)
    }

    fun onPress(position: Offset) {
        changeSelect(position.x, position.y)
        onChange(position)
    }

    fun dragStart(position: Offset) {
    }

    fun dragEnd() {
        rectIndicator.savePosition()
        ringIndicator.savePosition()
    }

    fun drag(position: Offset, dragAmount: Offset) {
        onChange(position)
    }

    fun paint(canvas: Canvas) {
        val nativeCanvas = canvas.nativeCanvas
        nativeCanvas.save()
        val dWidth = size.width - bound.left - bound.right - 2*borderSize
        val dHeight = size.height - bound.top - bound.bottom - 2*borderSize
        nativeCanvas.drawRect(bound.left, bound.top, dWidth + 2 * borderSize + bound.left, dHeight + 2 * borderSize + bound.top, borderPain)
        val start = borderSize+bound.left
        nativeCanvas.drawRect(start, start, dWidth + start, dHeight + start, huePaint)
        nativeCanvas.drawRect(start, start, dWidth + start, dHeight + start, saturationPaint)
        rectIndicator.draw(nativeCanvas,x.value,y.value)
        ringIndicator.draw(nativeCanvas,x.value,y.value)
        nativeCanvas.restore()
    }

    private fun getEffectiveX(x: Float):Float {
        val start = borderSize
        val dWidth = size.width - 2 * borderSize
        return min(max(x, start), dWidth)
    }

    private fun getEffectiveY(y: Float):Float {
        val start = borderSize
        val dHeight = size.height - 2 * borderSize
        return min(max(y, start), dHeight)
    }

    private fun changeSelect(x: Float, y: Float){
        if (rectIndicator.isInside(x, y)){
            if (!rectIndicator.isSelected){
                ringIndicator.isSelected = false
                rectIndicator.apply {
                    isSelected = true
                }
            }
        } else if (ringIndicator.isInside(x, y)) {
            if (!ringIndicator.isSelected){
                rectIndicator.isSelected = false
                ringIndicator.apply {
                    isSelected = true
                }
            }
        }
    }

    private fun onChange(position: Offset) {
        if (rectIndicator.isSelected){
            if (ringIndicator.isInside(position.x, position.y)){
                return
            }
        } else if (ringIndicator.isSelected){
            if (rectIndicator.isInside(position.x, position.y)){
                return
            }
        }
        val effectiveX = getEffectiveX(position.x)
        val effectiveY = getEffectiveY(position.y)
        x.value = effectiveX
        y.value = effectiveY
        rectIndicator.change(effectiveX, effectiveY)
        ringIndicator.change(effectiveX, effectiveY)
    }


}